﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Neoit.Utils.Extensions
{
    /// <summary>
    /// StringExtension
    /// </summary>
    public static partial class StringExtension
    {
        #region string是否有值,IsNotNullOrEmpty
        /// <summary>
        /// string是否有值,!IsNotNullOrEmpty
        /// </summary>
        public static bool Has(this string str)
        {
            return !string.IsNullOrEmpty(str);
        }
        /// <summary>
        /// string是否有值,IsNotNullOrEmpty
        /// </summary>
        public static bool NoHas(this string str)
        {
            return string.IsNullOrEmpty(str);
        }
        #endregion

        #region 正则
        /// <summary>
        /// 是否匹配
        /// </summary>
        public static bool IsMatch(this string str, string pattern)
        {
            if (str == null || pattern == null) return false;
            return Regex.IsMatch(str, pattern);
        }
        /// <summary>
        /// 是否匹配-忽略大小写
        /// </summary>
        public static bool IsMatchIgnoreCase(this string str, string pattern)
        {
            if (str == null || pattern == null) return false;
            return Regex.IsMatch(str, pattern, RegexOptions.IgnoreCase);
        }
        /// <summary>
        /// 正则包含
        /// </summary>
        public static bool ContainsRegex(this string str, string pattern)
        {
            return str.IsMatch(pattern);
        }
        /// <summary>
        /// 正则包含，符合多个(且)
        /// </summary>
        public static bool ContainsRegex(this string str, params string[] patterns)
        {
            return patterns.All(s => str.IsMatch(s));
        }
        /// <summary>
        /// 正则包含
        /// </summary>
        public static bool ContainsRegexIgnoreCase(this string str, string pattern)
        {
            return str.IsMatchIgnoreCase(pattern);
        }
        /// <summary>
        /// 正则匹配
        /// </summary>
        public static string Match(this string str, string pattern)
        {
            if (str == null || pattern == null) return null;
            return Regex.Match(str, pattern).Value;
        }
        /// <summary>
        /// 正则匹配多个
        /// </summary>
        public static IEnumerable<string> Matchs(this string str, string pattern)
        {
            if (str == null || pattern == null) return Enumerable.Empty<string>();
            return Regex.Matches(str, pattern).Select(s => s.Value);
        }
        /// <summary>
        /// 正则匹配-按组匹配, 当存在匹配时，index=0值为匹配的值，index=1为第一个标记匹配
        /// </summary>
        public static IEnumerable<string> MatchGroup(this string str, string pattern)
        {
            if (str == null || pattern == null) return Enumerable.Empty<string>();
            if (Regex.Match(str, pattern).Length == 0) return Enumerable.Empty<string>();
            return Regex.Match(str, pattern).Groups.Select(s => s.Value);
        }
        /// <summary>
        /// 正则匹配-组中第一个标记元素(括号标记):如a(\d+)b中的(\d+)
        /// </summary>
        public static string MatchGroupFirst(this string text, string pattern)
        {
            if (text == null) return null;
            var matchResult = text.MatchGroup(pattern);
            if (matchResult.Count() < 2) return null;
            return matchResult.ElementAt(1).Trim();
        }
        /// <summary>
        /// 正则替换
        /// </summary>
        public static string ReplaceRegex(this string str, string pattern, string replacement)
        {
            if (str == null || pattern == null || replacement == null) return null;
            return Regex.Replace(str, pattern, replacement);
        }
        /// <summary>
        /// 正则替换
        /// </summary>
        public static string ReplaceRegex(this string str, string pattern, string replacement, RegexOptions options = RegexOptions.None)
        {
            if (str == null || pattern == null || replacement == null) return null;
            return Regex.Replace(str, pattern, replacement, options);
        }
        /// <summary>
        /// 获取中间文本
        /// "a123b".Between("a", "b") >> "123"
        /// </summary>
        /// <param name="source"></param>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="containsTag">结果是否需要包含start、end标记字符</param>
        /// <returns></returns>
        public static string Between(this string source, string start, string end = null, bool containsTag = false)
        {
            if (source == null || (start == null && end == null)) return null;
            int startIndex = string.IsNullOrEmpty(start) ? 0 : source.IndexOf(start) + start.Length;
            int endIndex = string.IsNullOrEmpty(end) ? source.Length : source.IndexOf(end, startIndex);

            if (!string.IsNullOrEmpty(start) && source.IndexOf(start) < 0) return "";
            if (!string.IsNullOrEmpty(end) && source.IndexOf(end) < 0) return "";

            #region containsTag
            if (containsTag)
            {
                startIndex = startIndex - Convert.ToInt32(start?.Length);
                endIndex = endIndex + Convert.ToInt32(end?.Length);
            }
            #endregion

            if (startIndex == -1 || endIndex == -1 || startIndex >= endIndex) return string.Empty;
            return source.Substring(startIndex, endIndex - startIndex);
        }
        /// <summary>
        /// 正则获取中间文本 (?&lt;=X1).+?(?=X2)
        /// </summary>
        public static string BetweenRegex(this string str, string patternLeft, string patternRight = null, RegexOptions regexOptions = RegexOptions.None)
        {
            if (str == null || (patternLeft == null && patternRight == null)) return null;
            var result = Regex.Match(str, @$"(?<={patternLeft}).+?(?={patternRight})", regexOptions);
            if (!result.Success) return null;
            var left = patternLeft == null ? "" : $"(?<={patternLeft})";
            var right = patternRight == null ? "" : $"?(?={patternRight})";
            return Regex.Match(str, @$"{left}.+{right}", regexOptions).Value;
        }
        /// <summary>
        /// 正则获取中间文本 (?&lt;=X1).+?(?=X2) 忽略大小写
        /// </summary>
        public static string BetweenRegexIgnoreCase(this string str, string patternLeft, string patternRight = null)
        {
            return str.BetweenRegex(patternLeft, patternRight, RegexOptions.IgnoreCase);
        }
        #endregion

        #region 单词
        /// <summary>
        /// 首字母大写
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string FirstToUpper(this string str)
        {
            if (str == null) return null;
            if (str.Length > 1) return char.ToUpper(str[0]) + str[1..];
            return str.ToUpper();
        }
        /// <summary>
        /// 首字母小写
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string FirstToLower(this string str)
        {
            if (str == null) return null;
            if (str.Length > 1) return char.ToLower(str[0]) + str[1..];
            return str.ToLower();
        }
        #endregion

        #region 日期
        /// <summary>
        /// 格式化日期 (ios不兼容 ‘YYYY-MM-DD’)
        /// </summary>
        /// <param name="str"></param>
        /// <param name="formatType">默认 yyyy/MM/dd</param>
        /// <returns></returns>
        public static string FormatDate(this string str, string formatType = "yyyy/MM/dd")
        {
            if (string.IsNullOrEmpty(str)) return str;
            var date = Convert.ToDateTime(str);
            return date.ToString(formatType);
        }
        /// <summary>
        /// 格式化日期时间
        /// </summary>
        /// <param name="str"></param>
        /// <param name="formatType">默认 yyyy/MM/dd HH:mm:ss</param>
        /// <returns></returns>
        public static string FormatDateTime(this string str, string formatType = "yyyy/MM/dd HH:mm:ss")
        {
            return str.FormatDate(formatType);
        }
        #endregion

        #region 时间戳字符串转为时间
        /// <summary>
        /// 时间戳字符串（毫秒级）转时间
        /// </summary>
        public static DateTime? TimestampToTime(this string str)
        {
            if (string.IsNullOrEmpty(str)) return null;
            var startTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            return startTime.AddMilliseconds(long.Parse(str));
        }
        /// <summary>
        /// 时间戳字符串（秒级）转时间
        /// </summary>
        public static DateTime? TimestampSecondsToTime(this string str)
        {
            if (string.IsNullOrEmpty(str)) return null;
            return (str + "000").TimestampToTime();
        }
        #endregion

        #region 省略部分字符串 eg:北京多年来...
        /// <summary>
        /// 省略字符（保留指定数量字符，其余忽略） eg：北京多年来...
        /// </summary>
        /// <param name="str"></param>
        /// <param name="count">字符串长度(不包括省略符)</param>
        /// <param name="omit"></param>
        /// <returns></returns>
        public static string Omit(this string str, int count, string omit = "...")
        {
            if (string.IsNullOrEmpty(str)) return str;
            return str.Length > count ? str[..count] + omit : str;
        }
        /// <summary>
        /// 从开始位置截取，获取最大长度字符串(包括省略符)
        /// </summary>
        /// <param name="str"></param>
        /// <param name="maxLength">字符串最大长度(包括省略符)</param>
        /// <param name="omit">省略符</param>
        /// <returns></returns>
        public static string MaxLength(this string str, int maxLength, string omit = "...")
        {
            if (string.IsNullOrEmpty(str)) return str;
            omit = omit ?? "";
            if (maxLength <= omit.Length || str.Length <= maxLength) omit = "";
            return str.Omit(maxLength - omit.Length, omit);
        }
        #endregion

        #region 隐藏部分 eg:188****8888
        /// <summary>
        /// 隐藏部分 eg:188****8888
        /// </summary>
        /// <param name="str"></param>
        /// <param name="leftCount">左侧需要保留的字符数</param>
        /// <param name="rightCount">右侧需要保留的字符数</param>
        /// <param name="Omit">替换符号</param>
        /// <returns></returns>
        public static string Hidden(this string str, int leftCount, int rightCount, string Omit = "****")
        {
            if (string.IsNullOrEmpty(str)) return str;
            if (leftCount < 0 || rightCount < 0 || (leftCount + rightCount) >= str.Length) return str;
            return str[..leftCount] + Omit + str.Substring(str.Length - rightCount, rightCount);
        }
        #endregion

        #region 统一替换分隔符为逗号
        /// <summary>
        /// 统一替换分隔符(为默认的英文逗号)
        /// </summary>
        public static string ReplaceSeparator(this string val, string oldValuePattern = "[，$;；|、]", string newValue = ",")
        {
            if (string.IsNullOrEmpty(val)) return val;
            return Regex.Replace(val, oldValuePattern, newValue);

        }
        #endregion

        #region 低热度方法

        #region 分割字符串，且保留分割词
        /// <summary>
        /// 分割字符串，且保留分割词
        /// </summary>
        /// <param name="source"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        [Obsolete("please use [SplitReserveSeparator]")]
        public static List<string> SplitWithSeparator(this string source, string separator)
        {
            return source.SplitReserveSeparator(separator);
        }
        /// <summary>
        /// 分割字符串，且保留分割词
        /// </summary>
        /// <param name="source"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static List<string> SplitReserveSeparator(this string source, string separator)
        {
            var list = source.Split(separator, StringSplitOptions.RemoveEmptyEntries).ToList();
            if (!list.Any())
            {
                list.Add(separator);
                return list;
            }
            var count = list.Count - 1;
            for (int i = 0; i < count; i++)
            {
                list.Insert(1 + i * 2, separator);
            }
            return list;
        }
        #endregion

        #endregion

        #region 相似度
        /// <summary>
        /// 相似度：余弦相似算法
        /// 计算两个字符串的相似度
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public static double SimilarityCosine(this string str1, string str2)
        {
            if (string.IsNullOrEmpty(str1) || string.IsNullOrEmpty(str2)) return 0;
            HashSet<string> set1 = new HashSet<string>(ConvertToHashSet(str1));
            HashSet<string> set2 = new HashSet<string>(ConvertToHashSet(str2));

            HashSet<string> unionSet = new HashSet<string>(set1);
            unionSet.UnionWith(set2);

            int[] vector1 = GenerateVector(set1, unionSet);
            int[] vector2 = GenerateVector(set2, unionSet);

            double dotProduct = CalculateDotProduct(vector1, vector2);
            double magnitude1 = CalculateMagnitude(vector1);
            double magnitude2 = CalculateMagnitude(vector2);

            return Math.Round(dotProduct / (magnitude1 * magnitude2), 2);

            static HashSet<string> ConvertToHashSet(string str)
            {
                HashSet<string> hashSet = new HashSet<string>();

                for (int i = 0; i < str.Length; i++)
                {
                    hashSet.Add(str[i].ToString());
                }

                return hashSet;
            }

            static int[] GenerateVector(HashSet<string> set, HashSet<string> unionSet)
            {
                int[] vector = new int[unionSet.Count];

                int index = 0;
                foreach (string element in unionSet)
                {
                    vector[index] = set.Contains(element) ? 1 : 0;
                    index++;
                }

                return vector;
            }

            static double CalculateDotProduct(int[] vector1, int[] vector2)
            {
                double result = 0;

                for (int i = 0; i < vector1.Length; i++)
                {
                    result += vector1[i] * vector2[i];
                }

                return result;
            }

            static double CalculateMagnitude(int[] vector)
            {
                double result = 0;

                for (int i = 0; i < vector.Length; i++)
                {
                    result += Math.Pow(vector[i], 2);
                }

                return Math.Sqrt(result);
            }
        }
        #endregion

        #region Trim
        /// <summary>
        /// TrimStart
        /// </summary>
        public static string TrimStart(this string str, string trimStr)
        {
            if (str.StartsWith(trimStr))
            {
                return str[trimStr.Length..];
            }
            return str;
        }
        /// <summary>
        /// TrimStart
        /// </summary>
        public static string TrimEnd(this string str, string trimStr)
        {
            if (str.EndsWith(trimStr))
            {
                return str[..^trimStr.Length];
            }
            return str;
        }
        /// <summary>
        /// Trim
        /// </summary>
        public static string Trim(this string str, string trimStr)
        {
            if (str.StartsWith(trimStr))
            {
                str = str[trimStr.Length..];
            }
            if (str.EndsWith(trimStr))
            {
                str = str[..^trimStr.Length];
            }
            return str;
        }
        #endregion


    }
}
